package MAMDA;



import java.util.Calendar;
import java.util.LinkedHashSet;
import java.util.Set;
import java.util.logging.Level;
import java.util.logging.Logger;
import java.text.SimpleDateFormat;
import java.io.*;
import java.util.Date;

import GA.*;
import localSearch.*;
import Operations.ValuesGenerator;
import util.*;
import random.TestClusterBuilder;
import instrumentation.*;
import ReadWriteFieldsRelations.*;
public class MAMDA {
	protected static Logger logger = Logger.getLogger("Genetic Algorithm class.");	
	private GAInfo     gaInfo     = new GAInfo();
	private TestClusterBuilder TestCluster;
	private  TrackerCoverage track=  TrackerCoverage.getTracker();

	private  long startTime;
	private  long elapsed;
	 private static  long TestTimeout; //  60 * 500  Timeout at 30 seconds */
	 private static int  HillClimbingExecute=0;
	 int totalCoveredBranches=0;
	 static int maximumBranchNumber=Integer.MAX_VALUE;
	private void initialize() throws ClassNotFoundException,IOException{
		//Read the input file
		System.out.println("CUT is:" + GAConfig.classFilePath);
		 Set<Class<?>> allClasses = new LinkedHashSet<Class<?>>();
	      //get class from file
	      allClasses.addAll(TestClusterBuilder.readClassFromFile());

	      System.out.println("There are " + allClasses.size() + " classes as input");
		  if (GAConfig.SaveData) Log.log ("There are " + allClasses.size() + " classes as input");

	      System.out.println(allClasses);
	      Set<Class<?>> classesToTest = TestClusterBuilder.filterUntestableClasses(allClasses);
	      System.out.println("There are " + classesToTest.size() + " classes to test after filtering");
		  if (GAConfig.SaveData) Log.log ("There are " + classesToTest.size() + " classes to test after filtering");

	      System.out.println(classesToTest);
			if (GAConfig.SaveData) 
				Log.log("\n"+classesToTest.toString());


		// calculate the test cluster and the set of types involved in the
		 TestCluster = new TestClusterBuilder(classesToTest);	
		 TestCluster.toString();
//		 if (GAConfig.SaveData) Log.log (TestCluster.toString());
		 
		 gaInfo.setTestCluster(TestCluster);
	      MethodRelations relations = new MethodRelations(classesToTest);
	      relations.buildRelations();
	      gaInfo.setMethodRelations(relations);

		//Generate random population
		Chromosome[] population = Population.randomPopulationInitializer(TestCluster);
		
		gaInfo.setPopulation(population);
		
		this.gaInfo.setGeneration(0);
		this.gaInfo.setPopulationFitness(0);
	}
	private void execute() {
		//Calculate fitness of initial population

		System.out.println("\t **** execute generation "+ this.gaInfo.getGeneration() +" elapsed time: "+ elapsed + " of "+TestTimeout);

		Chromosome[] population=this.gaInfo.getPopulation();
		float populationTotalBRCovered=0;
		int i=0;
		if(this.gaInfo.getGeneration()!=0)
			i=(int)(((double)GAConfig.PopulationSize/(double)100) * GAConfig.elitismNumBest); 
		for( ; i<population.length && elapsed < TestTimeout && this.gaInfo.getNoExecution()<=GAConfig.fitnessEvaluation; i++) {
			Chromosome individual=population[i];
	//		System.out.println("GeneticAlgorithm.execute : "+individual.toString());
	//		this.gaInfo.setNoExecution((this.gaInfo.getNoExecution()+1));

			ReflectionExecutor.execute(this.gaInfo,individual);
			populationTotalBRCovered+= track.calculateFitness(individual);
			
			track.reset();
			elapsed = (Calendar.getInstance().getTimeInMillis() - startTime);
		}
		//calculate the total covered branches
		  totalCoveredBranches=track.getTotalCoveredBranch();
		 if (GAConfig.PrintPopulation && this.gaInfo.getGeneration()%1==0) Population.toString(gaInfo);
	
		 if(this.gaInfo.getPopulationFitness()<totalCoveredBranches){
			 	this.gaInfo.setPopulationFitness(totalCoveredBranches);
			 	this.gaInfo.setNoImprovementCount(0);
		 }
		 else
			 this.gaInfo.setNoImprovementCount(this.gaInfo.getNoImprovementCount() + 1);
		 
		System.out.println("\t Total Branch Covered= ( "+this.gaInfo.getPopulationFitness()+ " , "+ totalCoveredBranches +" )");
		
	}
	private void evolve()throws  IOException, ClassNotFoundException{
		LocalSearch HillClimbing = GAOperationsConfig.getLocalSearch();
		HillClimbing.setExtraInfo(gaInfo);
	    gaInfo.setMapBranchCondition(HillClimbing.readFileWGZ(gaInfo.getTestCluster()));
	    int noMinutes=1;
		for ( int it = 1; ((it<=10000000) && (elapsed < TestTimeout)&& this.gaInfo.getNoExecution()<=GAConfig.fitnessEvaluation) ; it++) {

			Chromosome[] population = Population.generateNewPopulation(this.gaInfo,TestCluster);
		
			gaInfo.setPopulation(population);
			this.gaInfo.setGeneration(this.gaInfo.getGeneration() + 1);	
			if(this.gaInfo.getNoImprovementCount()>=3) {
			//	System.out.println(this.gaInfo.getGeneration()+ "  Implement Local Search");
				this.gaInfo.setNoImprovementCount(0);
			//HillClimbing &&track.branchNotCoveredByIndividual.size()!=0
			for (int i = 0; (i < GAConfig.localSearchNum) && (elapsed < TestTimeout); i++) {
				HillClimbingExecute++;
				Chromosome improvedIndividual =HillClimbing.execute();
				if(improvedIndividual!=null)
					population[population.length-(1+i)]=improvedIndividual;
				elapsed = (Calendar.getInstance().getTimeInMillis() - startTime);
			}
			}

		this.execute();
			
	//	System.out.println("getGeneration:-2  "+this.gaInfo.getGeneration() + " size:"+ population[0].getSize());

			elapsed = (Calendar.getInstance().getTimeInMillis() - startTime);
			if((elapsed/60000)==noMinutes){
				 if (GAConfig.SaveData) Log.log("at " +  noMinutes +"  minute(s) Total Branch Covered= " + totalCoveredBranches );
				 noMinutes++;

			}
			if(totalCoveredBranches>=maximumBranchNumber)
				break;

		}


	}
	public void start() {
		try {
			startTime = Calendar.getInstance().getTimeInMillis();
			elapsed = Calendar.getInstance().getTimeInMillis() - startTime;
			this.gaInfo.setTestTimeOut(TestTimeout);
			this.gaInfo.setStartTime(startTime);

			this.initialize();
			//Calculate fitness of initial population
			 this.execute();
			//iteratively recombined and mutated to evolve successive populations
			 this.evolve();
			 System.out.println ("Done testing at generation: " + gaInfo.getGeneration());
			 System.out.println("Maximum test time exceeded at " + elapsed );			 
			 System.out.println("Maximum test execution " + this.gaInfo.getNoExecution() );			  

			 if (GAConfig.SaveData) Log.log ("Done testing at generation: " + gaInfo.getGeneration());
			 if (GAConfig.SaveData) Log.log("Maximum test time exceeded at " + elapsed );
			 if (GAConfig.SaveData) Log.log("Maximum No of invocations = " + gaInfo.getNoExecution() );
			
	//		 track.summarizeCoverage("Genetic Algorithm");
	
			System.out.println(track.toString());
			
			if (GAConfig.SaveData) {
				Log.log(" No of HillClimbing calls:  " + HillClimbingExecute);
				Log.log(track.toString());
			}
		 
		} catch (Exception e) {
			logger.log(Level.SEVERE, "Something went wrong: " + e.getMessage(), e);
		}
	
		
	}
	public static void writeLogFile(int i ){
		String logFilePath = null;
		String fileName = new SimpleDateFormat("MM_dd_yyyy_hhmm'.txt'")
			.format(new Date());
		
	String cName = GAConfig.classFilePath.substring(0,GAConfig.classFilePath.lastIndexOf("/") );
	cName = cName.substring(cName.lastIndexOf("/") +1);
	//cName=GAConfig.classFilePath;
	if(GAConfig.UseReadWrietRelation)
		if(GAConfig.useConstantSeeding)
				logFilePath = "./logFiles/"+ cName+ "_MAMDA_Seeding_"+i+ "_PopNo_" + GAConfig.PopulationSize+ "_"+ fileName;
		else
				logFilePath = "./logFiles/"+ cName+ "_MAMDA_NO_Seeding_"+i+ "_PopNo_" + GAConfig.PopulationSize+ "_"+ fileName;
	else
		logFilePath = "./logFiles/"+ cName+ "_MA_"+i+ "_PopNo_" + GAConfig.PopulationSize+ "_"+ fileName;

		
	if (logFilePath != null) {
		Log.logConfig(logFilePath);
//		experiment_configure_options(TestTimeout);
		System.out.println("Log file place: "+ new File(logFilePath).getAbsolutePath());
		}

		Log.log("       SUT               : "+ GAConfig.classFilePath);
		Log.log ("      Population Size   : "+GAConfig.PopulationSize);
		Log.log ("      TestCase Size     : "+GAConfig.TestCaseSize);
		Log.log ("      Elitism       : "+GAConfig.elitismNumBest);
		Log.log ("      Use read-write realtion   : "+GAConfig.UseReadWrietRelation);
		Log.log ("      Use constant seeding  : "+GAConfig.useConstantSeeding);

		Log.log ("      Timeout           : "+TestTimeout/60000 + " minutes  " + TestTimeout+" ms");
		Log.log ("");

	}
	private static void experiment_configure_options(long TestTimeout,int j) {
		Log.log("Start testing #..."+j);
		Log.log ("      Seed              : "+randomseed);
	}
	//the random seed
	public static long randomseed;// = (int) GAConfig.RANDOM_SEED;
	private void setTestTimeout(int i){
		if(i!=0)
		 TestTimeout = 60 * 1000*i;
		else
			 TestTimeout = 60 * 500;	 
	}
	public static void main(String [] args) throws IOException{
		if(args.length!=0){
			GAConfig.classFilePath=args[0];
			maximumBranchNumber=Integer.parseInt(args[1]);
		}
	//	maximumBranchNumber=10;
		GAConfig.setAlgorithmName("Memetic Algorithm");
		for(int i=0;i<=0;i+=6){
			
			MAMDA algorithm = new MAMDA();
			algorithm.setTestTimeout(i);
			if (GAConfig.SaveData)
				writeLogFile(i);
		     //set the random generation seeds
			
		      for (int j = 1; j <= 2; j += 1) {
					HillClimbingExecute=0;
		    	  System.out.println("Start testing #..."+j);
		    	  randomseed= System.currentTimeMillis();
				   	  ValuesGenerator.resetSeed(randomseed);

			   	  if (GAConfig.SaveData)
					experiment_configure_options(TestTimeout,j);

				algorithm.start();

				algorithm.track.setTrackerToNull();
				algorithm = new MAMDA();

			}
				if (GAConfig.SaveData)
					Log.log.close();
		}
//		System.exit(0);
		}
	
}
